#ifndef PW_PBL_VARIANT_HPP
#define PW_PBL_VARIANT_HPP

#include <variant>

namespace pw_pbl
{
	template <typename... T>
	struct Variant : public std::variant<T...>
	{
		using std::variant<T...>::variant;
		using std::variant<T...>::emplace;
		using std::variant<T...>::operator=;

		template <size_t I>
		constexpr decltype(auto) get() { return std::get<I>(*this); }
		template <size_t I>
		constexpr decltype(auto) get() const { return std::get<I>(*this); }
		template <typename I>
		constexpr decltype(auto) get() { return std::get<I>(*this); }
		template <typename I>
		constexpr decltype(auto) get() const { return std::get<I>(*this); }

		template <size_t I>
		constexpr decltype(auto) get_if() { return std::get_if<I>(this); }
		template <size_t I>
		constexpr decltype(auto) get_if() const { return std::get_if<I>(this); }
		template <typename I>
		constexpr decltype(auto) get_if() { return std::get_if<I>(this); }
		template <typename I>
		constexpr decltype(auto) get_if() const { return std::get_if<I>(this); }

		template <typename F>
		constexpr decltype(auto) visit(F &&f) { return std::visit(std::forward<F>(f), *this); }

		template <typename F>
		constexpr decltype(auto) visit(F &&f) const { return std::visit(std::forward<F>(f), *this); }

		template <typename V>
		constexpr decltype(auto) holds_alternative() const { return std::holds_alternative<V>(*this); }

		template <typename V>
		constexpr decltype(auto) emplace(V &&v) { return std::variant<T...>::template emplace<V>(std::forward<V>(v)); }
	};
} // namespace pw_pbl

template <typename... T>
struct std::variant_size<pw_pbl::Variant<T...>> : public std::integral_constant<std::size_t, sizeof...(T)>
{};

template <typename... T>
struct std::variant_size<const pw_pbl::Variant<T...>> : public std::integral_constant<std::size_t, sizeof...(T)>
{};

#endif // PW_PBL_VARIANT_HPP
